using System;
using System.IO;
using CodePlex.TfsLibrary.ObjectModel;
using CodePlex.TfsLibrary.RepositoryWebSvc;
using CodePlex.UnitTestUtility;
using Xunit;

namespace CodePlex.TfsLibrary.ClientEngine
{
    public class TfsStateTest
    {
        public class GetEntryList
        {
            [Fact]
            public void DeepRecursionDeleteTest()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar\Biff");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar\Biff\baz.txt", "This is the text");
                state.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/Foo/Bar", @"C:\Foo\Bar", 1, 1, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/Foo/Bar/Biff", @"C:\Foo\Bar\Biff", 1, 1, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\Bar\Biff\baz.txt", 2, 3, SourceItemStatus.Unmodified);

                state.FileSystem.DeleteDirectory(@"C:\Foo\Bar", true);

                TfsStateEntryList entryList = state.GetEntryList(@"C:\Foo\Bar\Biff");
                Assert.Equal(2, entryList.Count);
                Assert.NotNull(entryList["baz.txt"]);
                Assert.True(state.IsFolderTracked(@"C:\Foo\Bar"));
                Assert.True(state.IsFolderTracked(@"C:\Foo\Bar\Biff"));
                Assert.True(state.IsFileTracked(@"C:\Foo\Bar\Biff\baz.txt"));
            }

            [Fact]
            public void DeletingFolderStillHasInformation()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar\baz.txt", "This is the text");
                state.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/Foo/Bar", @"C:\Foo\Bar", 1, 1, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\Bar\baz.txt", 2, 3, SourceItemStatus.Unmodified);

                state.FileSystem.DeleteDirectory(@"C:\Foo\Bar", true);

                TfsStateEntryList entryList = state.GetEntryList(@"C:\Foo\Bar");
                Assert.Equal(2, entryList.Count);
                Assert.True(state.IsFileTracked(@"C:\Foo\Bar\baz.txt"));
            }
        }

        public class GetFolderInfo
        {
            [Fact]
            public void MissingFolder()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Null(state.GetFolderInfo(@"C:\Foo"));
            }

            [Fact]
            public void TrackedFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);

                TfsFolderInfo info = state.GetFolderInfo(@"C:\Foo");

                Assert.Equal("http://tfsUrl", info.TfsUrl);
                Assert.Equal("$/ServerPath/", info.ServerPath);
            }

            [Fact]
            public void UntrackedFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");

                Assert.Null(state.GetFolderInfo(@"C:\Foo"));
            }
        }

        public class GetItems
        {
            [Fact]
            public void NoMetadataThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.GetItems(@"C:\Foo");
                                                 });
            }
        }

        public class GetSelfEntry
        {
            [Fact]
            public void NoMetadataThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.GetSelfEntry(@"C:\Foo");
                                                 });
            }
        }

        public class GetSourceItem
        {
            [Fact]
            public void ItemDoesNotExist()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.GetSourceItem(@"C:\Baz\Bar");
                                                 });
            }
        }

        public class GetSourceItem_File
        {
            [Fact]
            public void Added()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "hi");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Add);

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");

                Assert.Equal(SourceItemStatus.Add, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Add, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void Conflicted()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "hi");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt.r5", "hi twice");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                state.MarkFileAsConflicted(@"C:\Foo\bar.txt", @"C:\Foo\bar.txt.r5", 5);

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");

                Assert.Equal(SourceItemStatus.Conflict, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Conflict, item.OriginalLocalItemStatus);
                Assert.Equal(5, item.LocalConflictChangesetId);
                Assert.Equal(@"C:\Foo\_tfs\text-base\bar.txt.tfs-conflict", item.LocalConflictTextBaseName);
            }

            [Fact]
            public void Deleted()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Delete);

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");

                Assert.Equal(SourceItemStatus.Delete, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Delete, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void FileNotInAWorkingFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Baz");
                state.FileSystem.WriteAllText(@"C:\Baz\bar.txt", "Text");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.GetSourceItem(@"C:\Baz\bar.txt");
                                                 });
            }

            [Fact]
            public void FileNotTracked_FolderTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "hi");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");

                Assert.Null(item.LocalTextBaseName);
                Assert.Equal(0, item.ItemId);
                Assert.Equal(0, item.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Unversioned, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unversioned, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void Missing_TrackedAdded()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "hi");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Add);
                state.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");

                Assert.Equal(SourceItemStatus.Missing, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Add, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void Missing_TrackedUnmodified()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "hi");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                state.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");

                Assert.Equal(SourceItemStatus.Missing, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unmodified, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void Modified_Tracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "hi");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "hi twice");

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");

                Assert.Equal(SourceItemStatus.Modified, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unmodified, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void UnmodifiedTrackedFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "hi");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");

                Assert.Equal(@"C:\Foo\bar.txt", item.LocalName);
                Assert.Equal(@"C:\Foo\_tfs\text-base\bar.txt.tfs-base", item.LocalTextBaseName);
                Assert.Equal(1, item.ItemId);
                Assert.Equal(2, item.LocalChangesetId);
                Assert.Equal(ItemType.File, item.ItemType);
                Assert.Equal(SourceItemStatus.Unmodified, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unmodified, item.OriginalLocalItemStatus);
            }
        }

        public class GetSourceItem_Folder
        {
            [Fact]
            public void Added()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Add);

                SourceItem item = state.GetSourceItem(@"C:\Foo\Bar");

                Assert.Equal(SourceItemStatus.Add, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Add, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void DeletedFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Delete);

                SourceItem item = state.GetSourceItem(@"C:\Foo\Bar");

                Assert.Equal(SourceItemStatus.Delete, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Delete, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void DeletedRootOfSourceTreeThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.FileSystem.DeleteDirectory(@"C:\Foo", true);

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.GetSourceItem(@"C:\Foo");
                                                 });
            }

            [Fact]
            public void FolderNotAWorkingFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Baz");
                state.FileSystem.EnsurePath(@"C:\Baz\Bar");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.GetSourceItem(@"C:\Baz\Bar");
                                                 });
            }

            [Fact]
            public void FolderNotTracked_ParentFolderTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                SourceItem item = state.GetSourceItem(@"C:\Foo\Bar");

                Assert.Equal(Constants.NullItemId, item.ItemId);
                Assert.Equal(Constants.NullChangesetId, item.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Unversioned, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unversioned, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void Missing_TrackedAdded()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Add);
                state.FileSystem.DeleteDirectory(@"C:\Foo\Bar", true);

                SourceItem item = state.GetSourceItem(@"C:\Foo\Bar");

                Assert.Equal(SourceItemStatus.Missing, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Add, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void Missing_TrackedUnmodified()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Unmodified);
                state.FileSystem.DeleteDirectory(@"C:\Foo\Bar", true);

                SourceItem item = state.GetSourceItem(@"C:\Foo\Bar");

                Assert.Equal(SourceItemStatus.Missing, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unmodified, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void MissingFolderTrackedAsDelete()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Delete);

                SourceItem item = state.GetSourceItem(@"C:\Foo\Bar");

                Assert.Equal(SourceItemStatus.Delete, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Delete, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void TrackedFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\");
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                SourceItem item = state.GetSourceItem(@"C:\Foo");

                Assert.Equal(@"C:\Foo", item.LocalName);
                Assert.Null(item.LocalTextBaseName);
                Assert.Equal(Constants.NullItemId, item.ItemId);
                Assert.Equal(Constants.NullChangesetId, item.LocalChangesetId);
                Assert.Equal(ItemType.Folder, item.ItemType);
                Assert.Equal(SourceItemStatus.Unmodified, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unmodified, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void TrackedRootFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\", 0, 0, SourceItemStatus.Unmodified);

                SourceItem item = state.GetSourceItem(@"C:\");

                Assert.Equal(SourceItemStatus.Unmodified, item.LocalItemStatus);
                Assert.Equal(SourceItemStatus.Unmodified, item.OriginalLocalItemStatus);
            }

            [Fact]
            public void UntrackedRootFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.GetSourceItem(@"C:\");
                                                 });
            }
        }

        public class GetSourceItems
        {
            [Fact]
            public void MissingDirectoryThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.GetSourceItems(@"C:\Foo");
                                                 });
            }

            [Fact]
            public void MixedElements()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.FileSystem.EnsurePath(@"C:\Foo\Baz");
                state.FileSystem.EnsurePath(@"C:\Foo\Bob");
                state.FileSystem.WriteAllText(@"C:\Foo\add.txt", "This is the contents of add");
                state.FileSystem.WriteAllText(@"C:\Foo\delete.txt", "This is the contents of delete");
                state.FileSystem.WriteAllText(@"C:\Foo\missing.txt", "This is the contents of missing");
                state.FileSystem.WriteAllText(@"C:\Foo\modified.txt", "This is the contents of modified");
                state.FileSystem.WriteAllText(@"C:\Foo\unmodified.txt", "This is the contents of unmodified");
                state.FileSystem.WriteAllText(@"C:\Foo\unversioned.txt", "This is the contents of unversioned");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/Bar/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Add);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/Biff/", @"C:\Foo\Biff", 0, 0, SourceItemStatus.Delete);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/Bob/", @"C:\Foo\Bob", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\add.txt", 1, 11, SourceItemStatus.Add);
                state.TrackFile(@"C:\Foo\delete.txt", 2, 12, SourceItemStatus.Delete);
                state.TrackFile(@"C:\Foo\missing.txt", 2, 12, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\modified.txt", 3, 13, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\unmodified.txt", 4, 14, SourceItemStatus.Unmodified);
                state.FileSystem.DeleteDirectory(@"C:\Foo\Bob", true);
                state.FileSystem.DeleteFile(@"C:\Foo\delete.txt");
                state.FileSystem.DeleteFile(@"C:\Foo\missing.txt");
                state.FileSystem.WriteAllText(@"C:\Foo\modified.txt", "This is the modified contents of modified");

                SourceItem[] items = state.GetSourceItems(@"C:\Foo");

                Assert.Equal(10, items.Length);
                Assert.Equal(@"C:\Foo\add.txt", items[0].LocalName);
                Assert.Equal(SourceItemStatus.Add, items[0].LocalItemStatus);
                Assert.Equal(@"C:\Foo\Bar", items[1].LocalName);
                Assert.Equal(SourceItemStatus.Add, items[1].LocalItemStatus);
                Assert.Equal(@"C:\Foo\Baz", items[2].LocalName);
                Assert.Equal(SourceItemStatus.Unversioned, items[2].LocalItemStatus);
                Assert.Equal(@"C:\Foo\Biff", items[3].LocalName);
                Assert.Equal(SourceItemStatus.Delete, items[3].LocalItemStatus);
                Assert.Equal(@"C:\Foo\Bob", items[4].LocalName);
                Assert.Equal(SourceItemStatus.Missing, items[4].LocalItemStatus);
                Assert.Equal(@"C:\Foo\delete.txt", items[5].LocalName);
                Assert.Equal(SourceItemStatus.Delete, items[5].LocalItemStatus);
                Assert.Equal(@"C:\Foo\missing.txt", items[6].LocalName);
                Assert.Equal(SourceItemStatus.Missing, items[6].LocalItemStatus);
                Assert.Equal(@"C:\Foo\modified.txt", items[7].LocalName);
                Assert.Equal(SourceItemStatus.Modified, items[7].LocalItemStatus);
                Assert.Equal(@"C:\Foo\unmodified.txt", items[8].LocalName);
                Assert.Equal(SourceItemStatus.Unmodified, items[8].LocalItemStatus);
                Assert.Equal(@"C:\Foo\unversioned.txt", items[9].LocalName);
                Assert.Equal(SourceItemStatus.Unversioned, items[9].LocalItemStatus);
            }

            [Fact]
            public void NonTrackedDirectoryThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.GetSourceItems(@"C:\Foo");
                                                 });
            }

            [Fact]
            public void NullDirectoryThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.GetSourceItems(null);
                                                     });
            }
        }

        public class IsFileTracked
        {
            [Fact]
            public void FileNotFound()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.False(state.IsFileTracked(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void FileNotTracked_FolderNotTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text here");

                Assert.False(state.IsFileTracked(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void FileNotTracked_FolderTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text here");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                Assert.False(state.IsFileTracked(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void FileTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text here");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);

                Assert.True(state.IsFileTracked(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void FileTrackedButNotOnTheFileSystem()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text here");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Delete);
                state.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                Assert.True(state.IsFileTracked(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void PassingTrackedFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Unmodified);

                Assert.False(state.IsFileTracked(@"C:\Foo\Bar"));
            }
        }

        public class IsFolderTracked
        {
            [Fact]
            public void FolderNotFound()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.False(state.IsFolderTracked(@"C:\Foo\Bar"));
            }

            [Fact]
            public void FolderNotTracked_ParentFolderNotTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");

                Assert.False(state.IsFolderTracked(@"C:\Foo\Bar"));
            }

            [Fact]
            public void FolderNotTracked_ParentFolderTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                Assert.False(state.IsFolderTracked(@"C:\Foo\Bar"));
            }

            [Fact]
            public void FolderTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Unmodified);

                Assert.True(state.IsFolderTracked(@"C:\Foo\Bar"));
            }

            [Fact]
            public void FolderTrackedButNotOnTheFileSystem()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Delete);
                state.FileSystem.DeleteDirectory(@"C:\Foo\Bar", true);

                Assert.True(state.IsFolderTracked(@"C:\Foo\Bar"));
            }

            [Fact]
            public void PassingTrackedFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text here");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);

                Assert.False(state.IsFolderTracked(@"C:\Foo\bar.txt"));
            }
        }

        public class MarkFileAsConflicted
        {
            [Fact]
            public void MarksFileAsConflicted()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "File contents");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.conflict", "Conflicted file contents");
                state.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 2, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 3, 4, SourceItemStatus.Unmodified);

                state.MarkFileAsConflicted(@"C:\Foo\bar.txt", @"C:\Foo\bar.conflict", 5);

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(SourceItemStatus.Conflict, item.LocalItemStatus);
                Assert.Equal(4, item.LocalChangesetId);
                Assert.Equal(5, item.LocalConflictChangesetId);
                Assert.Equal("File contents", state.FileSystem.ReadAllText(@"C:\Foo\_tfs\text-base\bar.txt.tfs-base"));
                Assert.Equal("Conflicted file contents", state.FileSystem.ReadAllText(@"C:\Foo\_tfs\text-base\bar.txt.tfs-conflict"));
                Assert.Equal(FileAttributes.ReadOnly, state.FileSystem.GetAttributes(@"C:\Foo\_tfs\text-base\bar.txt.tfs-conflict") & FileAttributes.ReadOnly);
            }

            [Fact]
            public void NonExistantFileThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.MarkFileAsConflicted(@"C:\Foo\bar.txt", @"C:\Foo\bar.txt", 1);
                                                 });
            }

            [Fact]
            public void NotExistantConflictFileThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "contents");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.MarkFileAsConflicted(@"C:\Foo\bar.txt", @"C:\Foo\baz.txt", 1);
                                                 });
            }

            [Fact]
            public void NullConflictFilenameThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.MarkFileAsConflicted(@"C:\Foo", null, 1);
                                                     });
            }

            [Fact]
            public void NullFilenameThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.MarkFileAsConflicted(null, @"C:\Foo", 1);
                                                     });
            }

            [Fact]
            public void ThrowsForNullChangesetId()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentException>(delegate
                                                 {
                                                     state.MarkFileAsConflicted(@"C:\Foo", @"C:\Foo", Constants.NullChangesetId);
                                                 });
            }

            [Fact]
            public void ThrowsIfDirectoryIsNotBeingTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "File contents");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.MarkFileAsConflicted(@"C:\Foo\bar.txt", @"C:\Foo\bar.txt", 1);
                                                 });
            }
        }

        public class MarkFileAsResolved
        {
            [Fact]
            public void MarksFileAsNotConflicted()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "text");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.conflict", "text 2");
                state.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 2, 3, SourceItemStatus.Unmodified);
                state.MarkFileAsConflicted(@"C:\Foo\bar.txt", @"C:\Foo\bar.conflict", 5);
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Manual resolve");

                state.MarkConflictedFileAsResolved(@"C:\Foo\bar.txt");

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(SourceItemStatus.Modified, item.LocalItemStatus);
                Assert.Equal(5, item.LocalChangesetId);
                Assert.Equal(Constants.NullChangesetId, item.LocalConflictChangesetId);
                Assert.Equal("Manual resolve", state.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
                Assert.Equal("text 2", state.FileSystem.ReadAllText(string.Format(@"C:\Foo\{0}\{1}\bar.txt{2}", TfsState.METADATA_FOLDER, TfsState.TEXT_BASE_FOLDER, TfsState.TEXT_BASE_EXTENSION)));
                Assert.False(state.FileSystem.FileExists(string.Format(@"C:\Foo\{0}\{1}\bar.txt{2}", TfsState.METADATA_FOLDER, TfsState.TEXT_BASE_FOLDER, TfsState.TEXT_BASE_EXTENSION_CONFLICT)));
                Assert.Null(item.LocalConflictTextBaseName);
            }

            [Fact]
            public void NonTrackedFileThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "text");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.MarkConflictedFileAsResolved(@"C:\Foo\bar.txt");
                                                 });
            }

            [Fact]
            public void NullFilenameThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.MarkConflictedFileAsResolved(null);
                                                     });
            }
        }

        public class RevertFile
        {
            [Fact]
            public void AddedFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Add);

                state.RevertFile(@"C:\Foo\bar.txt");

                Assert.False(state.IsFileTracked(@"C:\Foo\bar.txt"));
                Assert.Equal("Text", state.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void ConflictedFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text from 3");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt.r10", "Text from 10");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 3, SourceItemStatus.Unmodified);
                state.MarkFileAsConflicted(@"C:\Foo\bar.txt", @"C:\Foo\bar.txt.r10", 10);
                state.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                state.RevertFile(@"C:\Foo\bar.txt");

                SourceItem revertedItem = state.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(SourceItemStatus.Unmodified, revertedItem.LocalItemStatus);
                Assert.Equal(10, revertedItem.LocalChangesetId);
                Assert.Equal(Constants.NullChangesetId, revertedItem.LocalConflictChangesetId);
                Assert.Equal("Text from 10", state.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
                Assert.False(state.FileSystem.FileExists(@"C:\Foo\_tfs\text-base\bar.txt.tfs-conflict"));
                Assert.Null(revertedItem.LocalConflictTextBaseName);
            }

            [Fact]
            public void DeletedFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Delete);
                state.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                state.RevertFile(@"C:\Foo\bar.txt");

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(1, item.ItemId);
                Assert.Equal(2, item.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Unmodified, item.LocalItemStatus);
                Assert.Equal("Text", state.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void MissingFile_TrackedAdded()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Add);
                state.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                state.RevertFile(@"C:\Foo\bar.txt");

                Assert.False(state.FileSystem.FileExists(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void MissingFile_TrackedUnmodified()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                state.FileSystem.DeleteFile(@"C:\Foo\bar.txt");

                state.RevertFile(@"C:\Foo\bar.txt");

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(1, item.ItemId);
                Assert.Equal(2, item.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Unmodified, item.LocalItemStatus);
                Assert.Equal("Text", state.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
            }

            [Fact]
            public void ModifiedFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Biff!");

                state.RevertFile(@"C:\Foo\bar.txt");

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(@"C:\Foo\bar.txt", item.LocalName);
                Assert.Equal(1, item.ItemId);
                Assert.Equal(2, item.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Unmodified, item.LocalItemStatus);
                Assert.Equal("Text", state.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
                Assert.True((state.FileSystem.GetAttributes(@"C:\Foo\bar.txt") & FileAttributes.ReadOnly) == 0);
            }

            [Fact]
            public void ModifiedFileIsReadOnly()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Biff!");
                state.FileSystem.SetAttributes(@"C:\Foo\bar.txt", FileAttributes.ReadOnly);

                state.RevertFile(@"C:\Foo\bar.txt");

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(@"C:\Foo\bar.txt", item.LocalName);
                Assert.Equal(1, item.ItemId);
                Assert.Equal(2, item.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Unmodified, item.LocalItemStatus);
                Assert.Equal("Text", state.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
                Assert.True((state.FileSystem.GetAttributes(@"C:\Foo\bar.txt") & FileAttributes.ReadOnly) == 0);
            }

            [Fact]
            public void NullLocalPathThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.RevertFile(null);
                                                     });
            }

            [Fact]
            public void PassingFolderNameThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");

                Assert.Throws<ArgumentException>(delegate
                                                 {
                                                     state.RevertFile(@"C:\Foo\Bar");
                                                 });
            }

            [Fact]
            public void UnmodifiedFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);

                state.RevertFile(@"C:\Foo\bar.txt");

                // Nothing to check... the method does a non-throw NOOP
            }

            [Fact]
            public void UntrackedFileTrackedFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                state.RevertFile(@"C:\Foo\bar.txt");

                // Nothing to check... the method does a non-throw NOOP
            }

            [Fact]
            public void UntrackedFileUntrackedFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.RevertFile(@"C:\Foo\bar.txt");
                                                 });
            }
        }

        public class TrackFile
        {
            [Fact]
            public void AddsFileItemToEntryList()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "This is some sample text for the file.");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);

                state.TrackFile(@"C:\Foo\Bar.txt", 1, 2, SourceItemStatus.Add);

                TfsStateEntry[] entries = state.GetItems(@"C:\Foo");
                Assert.Equal(2, entries.Length);
                Assert.Equal("Bar.txt", entries[1].Name);
                Assert.Equal(1, entries[1].ItemId);
                Assert.Equal(2, entries[1].ChangesetId);
                Assert.Equal(SourceItemStatus.Add, entries[1].Status);
                Assert.Equal(ItemType.File, entries[1].ItemType);
                Assert.Null(entries[1].TfsServerUrl);
                Assert.Null(entries[1].ServerPath);
            }

            [Fact]
            public void CannotCallTrackFileAgainstAConflictedFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "This is some sample text for the file.");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);
                state.TrackFile(@"C:\Foo\Bar.txt", 1, 2, SourceItemStatus.Unmodified);
                state.MarkFileAsConflicted(@"C:\Foo\Bar.txt", @"C:\Foo\Bar.txt", 5);

                Assert.Throws<InvalidOperationException>(delegate
                                                         {
                                                             state.TrackFile(@"C:\Foo\Bar.txt", 1, 6, SourceItemStatus.Unmodified);
                                                         });
            }

            [Fact]
            public void CannotTrackFileIntoConflictedState()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "This is some sample text for the file.");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);

                Assert.Throws<ArgumentException>(delegate
                                                 {
                                                     state.TrackFile(@"C:\Foo\Bar.txt", 1, 2, SourceItemStatus.Conflict);
                                                 });
            }

            [Fact]
            public void CopiesReadOnlyFileContentsIntoTextBaseDirectory()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "This is some sample text for the file.");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);

                state.TrackFile(@"C:\Foo\Bar.txt", 1, 2, SourceItemStatus.Add);

                string copiedFilename = string.Format(@"C:\Foo\{0}\{1}\Bar.txt{2}", TfsState.METADATA_FOLDER, TfsState.TEXT_BASE_FOLDER, TfsState.TEXT_BASE_EXTENSION);
                Assert.True(state.FileSystem.FileExists(copiedFilename));
                Assert.True((state.FileSystem.GetAttributes(copiedFilename) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly);
            }

            [Fact]
            public void DeleteDoesNotThrowWhenFileNotPresent()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Delete);
            }

            [Fact]
            public void NonExistantFileThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.TrackFolder("http://tfsUrl", "$/ServerPath", @"C:\Foo", 1, 2, SourceItemStatus.Unmodified);

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.TrackFile(@"C:\Foo\Bar.txt", 0, 0, SourceItemStatus.Add);
                                                 });
            }

            [Fact]
            public void NullFilenameThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.TrackFile(null, 0, 0, SourceItemStatus.Add);
                                                     });
            }

            [Fact]
            public void ThrowsIfDirectoryIsNotBeingTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "Text");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.TrackFile(@"C:\Foo\Bar.txt", 0, 0, SourceItemStatus.Add);
                                                 });
            }

            [Fact]
            public void TrackFileAllowsAlternativeTextBase()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.FileSystem.WriteAllText(@"C:\Foo\baz.txt", "Other Text");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                state.TrackFile(@"C:\Foo\bar.txt", @"C:\Foo\baz.txt", 1, 2, SourceItemStatus.Unmodified);

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(@"C:\Foo\bar.txt", item.LocalName);
                Assert.Equal(1, item.ItemId);
                Assert.Equal(2, item.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Modified, item.LocalItemStatus);
                Assert.Equal("Text", state.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
                Assert.Equal("Other Text", state.FileSystem.ReadAllText(item.LocalTextBaseName));
            }

            [Fact]
            public void TrackFileTwiceWithUnmodifiedUpdatesTextBase()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\bar.txt", 1, 2, SourceItemStatus.Unmodified);

                state.FileSystem.WriteAllText(@"C:\Foo\bar.txt", "Other Text");
                state.TrackFile(@"C:\Foo\bar.txt", 1, 3, SourceItemStatus.Unmodified);

                SourceItem item = state.GetSourceItem(@"C:\Foo\bar.txt");
                Assert.Equal(@"C:\Foo\bar.txt", item.LocalName);
                Assert.Equal(1, item.ItemId);
                Assert.Equal(3, item.LocalChangesetId);
                Assert.Equal(SourceItemStatus.Unmodified, item.LocalItemStatus);
                Assert.Equal("Other Text", state.FileSystem.ReadAllText(@"C:\Foo\bar.txt"));
            }
        }

        public class TrackFolder
        {
            [Fact]
            public void AddsEntryToParentWhenParentIsBeingTracked()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);

                state.TrackFolder("http://tfsUrl", "$/ServerPath/Bar/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Add);

                TfsStateEntry[] entries = state.GetItems(@"C:\Foo");
                Assert.Equal(2, entries.Length);
                Assert.Equal("", entries[0].Name);
                Assert.Equal("Bar", entries[1].Name);
                Assert.Equal(null, entries[1].TfsServerUrl);
                Assert.Equal(null, entries[1].ServerPath);
                Assert.Equal(SourceItemStatus.Add, entries[1].Status);
                Assert.Equal(Constants.NullChangesetId, entries[1].ChangesetId);
                Assert.Equal(Constants.NullItemId, entries[1].ItemId);
                Assert.Equal(ItemType.Folder, entries[1].ItemType);
            }

            [Fact]
            public void CreatesMetadataOnFileSystem()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");

                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);

                Assert.True(state.FileSystem.DirectoryExists(@"C:\Foo\" + TfsState.METADATA_FOLDER));
                Assert.True(state.FileSystem.FileExists(@"C:\Foo\" + TfsState.METADATA_FOLDER + @"\" + TfsState.ENTRIES_FILENAME));

                TfsStateEntry entry = state.GetSelfEntry(@"C:\Foo");

                Assert.NotNull(entry);
                Assert.Equal("http://tfsUrl", entry.TfsServerUrl);
                Assert.Equal("$/ServerPath/", entry.ServerPath);
                Assert.Equal(SourceItemStatus.Add, entry.Status);
                Assert.Equal(Constants.NullChangesetId, entry.ChangesetId);
                Assert.Equal(Constants.NullItemId, entry.ItemId);
                Assert.Equal(ItemType.Folder, entry.ItemType);
                Assert.Equal("", entry.Name);
            }

            [Fact]
            public void NewlyTrackingFolderCreatesNewShadow()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");

                state.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);

                Assert.NotNull(state.GetEntryList(@"C:\Foo")[""].Shadow);
            }

            [Fact]
            public void NormalizesServerPathToEndWithSlash()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");

                state.TrackFolder("http://tfsUrl", "$/ServerPath", @"C:\Foo", 0, 0, SourceItemStatus.Add);

                Assert.Equal("$/ServerPath/", state.GetSelfEntry(@"C:\Foo").ServerPath);
            }

            [Fact]
            public void NullFolderThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.TrackFolder("http://tfsUrl", "$/ServerPath/", null, 0, 0, SourceItemStatus.Add);
                                                     });
            }

            [Fact]
            public void NullServerPathThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.TrackFolder("http://tfsUrl", null, @"C:\Foo", 0, 0, SourceItemStatus.Add);
                                                     });
            }

            [Fact]
            public void NullTfsUrlThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.TrackFolder(null, "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);
                                                     });
            }

            [Fact]
            public void RetrackingFolderDoesNotGenerateNewShadow()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);
                string oldShadow = state.GetEntryList(@"C:\Foo")[""].Shadow;

                state.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);

                Assert.Equal(oldShadow, state.GetEntryList(@"C:\Foo")[""].Shadow);
            }

            [Fact]
            public void SecondTimeUpdatesExistingMetadata()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");

                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                Assert.Equal(SourceItemStatus.Unmodified, state.GetSelfEntry(@"C:\Foo").Status);
            }

            [Fact]
            public void ThrowsIfFolderDoesNotExist()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);
                                                 });
            }

            [Fact]
            public void TrackingChildFolderWritesProperShadowValueToParent()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/Foo", @"C:\Foo", 1, 1, SourceItemStatus.Unmodified);

                state.TrackFolder("http://tfsUrl", "$/Foo/Bar", @"C:\Foo\Bar", 1, 1, SourceItemStatus.Unmodified);

                Assert.Equal(state.GetEntryList(@"C:\Foo\Bar")[""].Shadow, state.GetEntryList(@"C:\Foo")["Bar"].Shadow);
            }
        }

        public class UntrackFile
        {
            [Fact]
            public void DeletesTextBaseConflictFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "This is some sample text for the file.");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);
                state.TrackFile(@"C:\Foo\Bar.txt", 1, 2, SourceItemStatus.Add);
                state.MarkFileAsConflicted(@"C:\Foo\Bar.txt", @"C:\Foo\Bar.txt", 19);

                state.UntrackFile(@"C:\Foo\Bar.txt");

                Assert.False(state.FileSystem.FileExists(string.Format(@"C:\Foo\{0}\{1}\Bar.txt{2}", TfsState.METADATA_FOLDER, TfsState.TEXT_BASE_FOLDER, TfsState.TEXT_BASE_EXTENSION_CONFLICT)));
            }

            [Fact]
            public void DeletesTextBaseFile()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "This is some sample text for the file.");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Add);
                state.TrackFile(@"C:\Foo\Bar.txt", 1, 2, SourceItemStatus.Add);

                state.UntrackFile(@"C:\Foo\Bar.txt");

                Assert.False(state.FileSystem.FileExists(string.Format(@"C:\Foo\{0}\{1}\Bar.txt{2}", TfsState.METADATA_FOLDER, TfsState.TEXT_BASE_FOLDER, TfsState.TEXT_BASE_EXTENSION)));
            }

            [Fact]
            public void NonTrackedFileDoesNotChangeEntryList()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                int count = state.GetItems(@"C:\Foo").Length;

                state.UntrackFile(@"C:\Foo\Bar.txt");

                Assert.Equal(count, state.GetItems(@"C:\Foo").Length);
            }

            [Fact]
            public void NonWorkingDirectoryThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "Text");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.UntrackFile(@"C:\Foo\Bar.txt");
                                                 });
            }

            [Fact]
            public void NullFilenameThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.UntrackFile(null);
                                                     });
            }

            [Fact]
            public void TrackedFileRemovesItemFromEntryList()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.WriteAllText(@"C:\Foo\Bar.txt", "Text");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFile(@"C:\Foo\Bar.txt", 1, 2, SourceItemStatus.Unmodified);
                int count = state.GetItems(@"C:\Foo").Length;

                state.UntrackFile(@"C:\Foo\Bar.txt");

                Assert.Equal(count - 1, state.GetItems(@"C:\Foo").Length);
            }
        }

        public class UntrackFolder
        {
            [Fact]
            public void DeletesMetadataFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);

                state.UntrackFolder(@"C:\Foo");

                Assert.False(state.IsFolderTracked(@"C:\Foo"));
            }

            [Fact]
            public void DeletesMetadataFoldersForSubDirectories()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar\Baz");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/Bar/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/Bar/Baz/", @"C:\Foo\Bar\Baz", 0, 0, SourceItemStatus.Unmodified);

                state.UntrackFolder(@"C:\Foo");

                Assert.False(state.IsFolderTracked(@"C:\Foo"));
                Assert.False(state.IsFolderTracked(@"C:\Foo\Bar"));
                Assert.False(state.IsFolderTracked(@"C:\Foo\Bar\Baz"));
            }

            [Fact]
            public void MissingFolder()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");
                state.FileSystem.EnsurePath(@"C:\Foo\Bar");
                state.TrackFolder("http://tfsUrl", "$/ServerPath/", @"C:\Foo", 0, 0, SourceItemStatus.Unmodified);
                state.TrackFolder("http://tfsUrl", "$/ServerPath/Bar/", @"C:\Foo\Bar", 0, 0, SourceItemStatus.Unmodified);
                state.FileSystem.DeleteDirectory(@"C:\Foo\Bar", true);

                state.UntrackFolder(@"C:\Foo\Bar");

                Assert.False(state.IsFolderTracked(@"C:\Foo\Bar"));
            }

            [Fact]
            public void NonTrackedDirectoryThrows()
            {
                TestableTfsState state = TestableTfsState.Create();
                state.FileSystem.EnsurePath(@"C:\Foo");

                Assert.Throws<TfsStateException>(delegate
                                                 {
                                                     state.UntrackFolder(@"C:\Foo");
                                                 });
            }

            [Fact]
            public void NullDirectoryThrows()
            {
                TestableTfsState state = TestableTfsState.Create();

                Assert.Throws<ArgumentNullException>(delegate
                                                     {
                                                         state.UntrackFolder(null);
                                                     });
            }
        }

        class TestableTfsState : TfsState
        {
            public readonly MockFileSystem FileSystem;

            TestableTfsState(MockFileSystem fileSystem)
                : base(fileSystem)
            {
                FileSystem = fileSystem;
            }

            public static TestableTfsState Create()
            {
                return new TestableTfsState(new MockFileSystem());
            }

            public new TfsStateEntryList GetEntryList(string directory)
            {
                return base.GetEntryList(directory);
            }
        }
    }
}